home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / CW GUSI 1.6.4 / src / GUSIUDP.cp < prev    next >
Text File  |  1995-06-03  |  12KB  |  479 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIUDP.cp        -    UDP Datagram Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by 
  7.     
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.           
  11. Language    :    MPW C/C++
  12.  
  13. $Log: GUSIUDP.cp,v $
  14. Revision 1.3  1994/12/30  20:19:35  neeri
  15. Wake up process from completion routines.
  16.  
  17. Revision 1.2  1994/08/10  00:02:50  neeri
  18. Sanitized for universal headers.
  19.  
  20. Revision 1.1  1994/02/25  02:31:00  neeri
  21. Initial revision
  22.  
  23. Revision 0.5  1994/02/15  00:00:00  neeri
  24. udp_read_ahead_done didn't record the packet source
  25.  
  26. Revision 0.4  1993/06/21  00:00:00  neeri
  27. forgot to reset asyncerr
  28.  
  29. Revision 0.3  1993/06/17  00:00:00  neeri
  30. getsockname() didn't work for some sorts of bind()
  31.  
  32. Revision 0.2  1992/09/14  00:00:00  neeri
  33. select() didn't return enough goodies.
  34.  
  35. Revision 0.1  1992/08/25  00:00:00  neeri
  36. Started putting some real work in
  37.  
  38. *********************************************************************/
  39.  
  40. #include "GUSIINET_P.h"
  41.  
  42. #if GENERATING68K
  43. #pragma segment GUSIINET
  44. #endif
  45.  
  46. /********************** Completion procedures ***********************/
  47.  
  48. void udp_read_ahead_done(AnnotatedPB *pb)
  49. {
  50.     UDPSocket *        sock    =    (UDPSocket *) pb->Owner();
  51.     UDPiopb *        udp    =    pb->UDP();
  52.  
  53.     if (!udp->ioResult) {
  54.         sock->recvBuf    = udp->csParam.receive.rcvBuff;
  55.         sock->recvd        = udp->csParam.receive.rcvBuffLen;
  56.     } else {
  57.         sock->recvBuf    = nil;
  58.         sock->recvd        = 0;
  59.     }
  60.     sock->asyncerr                 =     udp->ioResult;
  61.     sock->peer.sin_len             = sizeof(struct sockaddr_in);
  62.     sock->peer.sin_family         = AF_INET;
  63.     sock->peer.sin_addr.s_addr    =    udp->csParam.receive.remoteHost;
  64.     sock->peer.sin_port            =    udp->csParam.receive.remotePort;
  65.     sock->Ready();
  66. }
  67.  
  68. #if GENERATINGCFM
  69. RoutineDescriptor    u_udp_read_ahead_done = 
  70.         BUILD_ROUTINE_DESCRIPTOR(uppUDPIOCompletionProcInfo, udp_read_ahead_done);
  71. #else
  72. #define u_udp_read_ahead_done udp_read_ahead_done
  73. #endif
  74.  
  75. void udp_send_done(AnnotatedPB *pb)
  76. {    
  77.     UDPSocket *        sock    =    (UDPSocket *) pb->Owner();
  78.     UDPiopb *        udp    =    pb->UDP();
  79.     
  80.     ((miniwds *)udp->csParam.send.wdsPtr)->terminus    = udp->ioResult;
  81.     ((miniwds *)udp->csParam.send.wdsPtr)->ptr        = nil;
  82.     
  83.     sock->Ready();
  84. }
  85.  
  86. #if GENERATINGCFM
  87. RoutineDescriptor    u_udp_send_done = 
  88.         BUILD_ROUTINE_DESCRIPTOR(uppUDPIOCompletionProcInfo, udp_send_done);
  89. #else
  90. #define u_udp_send_done udp_send_done
  91. #endif
  92.  
  93. /************************ UDPSocket members *************************/
  94.  
  95. UDPSocket::UDPSocket()
  96.     :    INETSocket()
  97. {
  98.     sstate = SOCK_STATE_NO_STREAM;
  99. }
  100.  
  101. UDPSocket::UDPSocket(StreamPtr stream)
  102.     :    INETSocket(stream)
  103. {
  104.     OSErr            err;
  105.     UDPiopb *    pb;
  106.  
  107.     pb                                                = GetPB();
  108.     pb->ioCompletion                            = UDPIOCompletionUPP(&u_udp_read_ahead_done);
  109.     pb->csCode                                     = UDPRead;
  110.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  111.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  112.  
  113.     /* We know that there is a read pending, so we make a synchronous call */
  114.     
  115.     if (err = PBControlSync(ParmBlkPtr(pb)))
  116.         TCP_error(err);
  117.     
  118.     if (!pb->ioResult) {
  119.         recvBuf    = pb->csParam.receive.rcvBuff;
  120.         recvd        = pb->csParam.receive.rcvBuffLen;
  121.     } else {
  122.         recvBuf    = nil;
  123.         recvd        = 0;
  124.     }
  125.     asyncerr                 =    pb->ioResult;
  126.     peer.sin_len             = sizeof(struct sockaddr_in);
  127.     peer.sin_family         = AF_INET;
  128.     peer.sin_addr.s_addr    =    pb->csParam.receive.remoteHost;
  129.     peer.sin_port            =    pb->csParam.receive.remotePort;
  130. }
  131.  
  132. UDPSocket::~UDPSocket()
  133. {
  134.     UDPiopb *    pb;
  135.     
  136.     if (sstate == SOCK_STATE_NO_STREAM)
  137.         return;
  138.     
  139.     pb                    = GetPB();    
  140.     pb->csCode         = UDPRelease;
  141.     
  142.     if (!PBControlSync(ParmBlkPtr(pb)))
  143.         DisposPtr(pb->csParam.create.rcvBuff);
  144. }
  145.  
  146. UDPiopb * UDPSocket::GetPB()
  147. {
  148.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  149.     pb->UDP()->ioCRefNum =    INETSockets.Driver();
  150.     pb->UDP()->udpStream    =    stream;
  151.     
  152.     pb->SetOwner(this);
  153.     
  154.     return pb->UDP();
  155. }
  156.  
  157. unsigned long UDPSocket::Available()
  158. {
  159.     return (asyncerr == inProgress) ? 0 : recvd;
  160. }
  161.  
  162. int UDPSocket::NewStream()
  163. {
  164.     OSErr            err;
  165.     UDPiopb *    pb;
  166.  
  167.     pb                                        = GetPB();
  168.     pb->csCode                             = UDPCreate;
  169.     pb->csParam.create.rcvBuff     = (char *)NewPtr(STREAM_BUFFER_SIZE);
  170.     pb->csParam.create.rcvBuffLen    = STREAM_BUFFER_SIZE;
  171.     pb->csParam.create.notifyProc    = NULL;
  172.     pb->csParam.create.localPort     = sa.sin_port;
  173.     
  174.     if (err = PBControlSync(ParmBlkPtr(pb)))
  175.         return TCP_error(err);
  176.         
  177.     stream         = pb->udpStream;
  178.     sa.sin_port = pb->csParam.create.localPort;
  179.     
  180.     sstate     = SOCK_STATE_UNCONNECTED;
  181.     recvd     = 0;
  182.     recvBuf     = 0;
  183.     asyncerr    = inProgress;
  184.  
  185.     return ReadAhead();
  186. }
  187.  
  188. int UDPSocket::FlushReadAhead()
  189. {
  190.     OSErr            err;
  191.     UDPiopb *    pb;
  192.  
  193.     /* flush the read-ahead buffer if its not from our new friend */
  194.     pb                                        = GetPB();
  195.     pb->ioCompletion                    = nil;
  196.     pb->csCode                             = UDPBfrReturn;
  197.     pb->csParam.receive.rcvBuff    = recvBuf;
  198.     recvBuf                                = 0;
  199.     recvd                                   = 0;
  200.     
  201.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  202.         return TCP_error(err);
  203.     else
  204.         return 0;
  205. }
  206.  
  207. int UDPSocket::ReadAhead()
  208. {
  209.     OSErr            err;
  210.     UDPiopb *    pb;
  211.  
  212.     pb                                                = GetPB();
  213.     pb->ioCompletion                            = UDPIOCompletionUPP(&u_udp_read_ahead_done);
  214.     pb->csCode                                     = UDPRead;
  215.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  216.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  217.     asyncerr                                        = 1;
  218.     
  219.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  220.         return TCP_error(asyncerr = err);
  221.     
  222.     return 0;
  223. }
  224.  
  225. /*
  226.  * bind(addr, namelen)
  227.  * 
  228.  * As an extra feature over the standard INET bind, we create a new stream if necessary.
  229.  * if the stream is not created, we can't receive any packets yet.
  230.  */
  231.  
  232. int UDPSocket::bind(void * addr, int namelen)
  233. {
  234.     if (INETSocket::bind(addr, namelen) || NewStream())
  235.         return -1;
  236.     else
  237.         return 0;
  238. }
  239.  
  240. /*
  241.  *    getsockname(name, namelen)
  242.  *
  243.  *        getsockname returns the current name for the  socket.
  244.  *        Namelen should  be initialized to
  245.  *        indicate the amount of space pointed to by name.  On  return
  246.  *        it contains the actual size of the name returned (in bytes).
  247.  *        
  248.  *        A 0 is returned if the call succeeds, -1 if it fails.
  249.  */
  250.  
  251. int UDPSocket::getsockname(void *name, int *namelen)
  252. {
  253.     if (sstate == SOCK_STATE_NO_STREAM)
  254.         if (NewStream())
  255.             return -1;
  256.  
  257.     return INETSocket::getsockname(name, namelen);
  258. }
  259.  
  260. /*
  261.  *    UDPSocket::connect - initiate a connection on a MacTCP socket
  262.  *
  263.  *        This  call specifies the address to  which  datagrams
  264.  *        are  to  be  sent, and the only address from which datagrams
  265.  *        are to be received.  
  266.  *             
  267.  *        UDP sockets may use connect() multiple times to change
  268.  *        their association. UDP sockets may dissolve the association
  269.  *        by connecting to an invalid address, such as a null
  270.  *        address.
  271.  *        
  272.  *        If the connection or binding succeeds, then 0 is returned.
  273.  *        Otherwise a -1 is returned, and a more specific error code
  274.  *        is stored in errno.
  275.  *        
  276.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  277.  *        
  278.  *        EHOSTUNREACH        The TCP connection came up half-way and 
  279.  *                          then failed.
  280.  */
  281.  
  282. int UDPSocket::connect(void * address, int addrlen)
  283. {
  284.     struct sockaddr_in *    addr    = (struct sockaddr_in *) address;
  285.         
  286.     if (addrlen != int(sizeof(struct sockaddr_in)))
  287.         return GUSI_error(EINVAL);
  288.  
  289.     if (addr->sin_family != AF_INET)
  290.         return GUSI_error(EAFNOSUPPORT);
  291.     
  292.     /* make the stream if its not made already */
  293.     if (sstate == SOCK_STATE_NO_STREAM) {
  294.         if (NewStream())
  295.             return -1;
  296.     } else if (recvBuf)
  297.         if (FlushReadAhead())
  298.             return -1;
  299.     
  300.     /* record our peer */
  301.     peer.sin_len             = sizeof(struct sockaddr_in);
  302.     peer.sin_addr.s_addr    = addr->sin_addr.s_addr;
  303.     peer.sin_port             = addr->sin_port;
  304.     peer.sin_family         = AF_INET;
  305.     sstate                     = SOCK_STATE_CONNECTED;
  306.     
  307.     return 0;
  308. }
  309.  
  310. /*    
  311.  *    UDPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  312.  *        
  313.  *        recv() and recvfrom() attempt to receive a message (ie a datagram) 
  314.  *        on the socket s. 
  315.  *
  316.  *        from returns the address of the socket which sent the message.
  317.  *        fromlen is the usual value-result length parameter. 
  318.  *        
  319.  *        Typically, read() is used with a TCP stream and recv() with
  320.  *        UDP where the idea of a message makes more sense. But in fact,
  321.  *        read() and recv() are equivalent.
  322.  *        
  323.  *        If a message (ie. datagram) is too long to fit in the supplied 
  324.  *        buffer, excess bytes will be discarded..
  325.  *
  326.  *        If no messages are available at the socket, the receive call
  327.  *        waits for a message to arrive, unless the socket is non-
  328.  *        blocking in which case -1 is returned with errno set to 
  329.  *        EWOULDBLOCK.
  330.  *
  331.  *        Flags is ignored.
  332.  *        
  333.  *        If successful, the number of bytes actually received is
  334.  *        returned. Otherwise, a -1 is returned and the global variable
  335.  *        errno is set to indicate the error. 
  336.  *        
  337.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  338.  */
  339.  
  340. int UDPSocket::recvfrom(void * buffer, int buflen, int, void * from, int * fromlen)
  341. {
  342.     /* make the stream if its not made already */
  343.     if (sstate == SOCK_STATE_NO_STREAM)
  344.         if (NewStream())
  345.             return -1;
  346.             
  347.     /* dont block a non-blocking socket */
  348.     if (nonblocking && asyncerr == 1)
  349.         return GUSI_error(EWOULDBLOCK);
  350.     
  351.     SPIN(asyncerr == 1, SP_DGRAM_READ,0);
  352.  
  353.     if (asyncerr!=noErr)
  354.         return TCP_error(asyncerr);
  355.  
  356.     /* return the data to the user - truncate the packet if necessary */
  357.     buflen = min(buflen,recvd);    
  358.     BlockMove(recvBuf, buffer, buflen);
  359.  
  360.     if (from && *fromlen >= int(sizeof(struct sockaddr_in))) {
  361.         *(struct sockaddr_in *) from = peer;
  362.         *fromlen = sizeof (struct sockaddr_in);
  363.     }    
  364.     
  365.     /* continue the read-ahead - errors which occur */
  366.     /* here will show up next time around */
  367.     
  368.     FlushReadAhead();
  369.     ReadAhead();
  370.     
  371.     return buflen;
  372. }
  373.  
  374. /*
  375.  *    UDPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  376.  *        sendto() is used to transmit a message to another 
  377.  *        socket on the socket s.
  378.  *
  379.  *        Typically, write() is used with a TCP stream and send() with
  380.  *        UDP where the idea of a message makes more sense. But in fact,
  381.  *        write() and send() are equivalent.
  382.  *
  383.  *    Write() and send() operations are completed as soon as the
  384.  *    data is placed on the transmission queue.
  385.  *
  386.  *        The address of the target is given by to.
  387.  *
  388.  *        The message must be short enough to fit into one datagram.
  389.  *        
  390.  *        Buffer space must be available to hold the message to be 
  391.  *        transmitted, regardless of its non-blocking I/O state.
  392.  *
  393.  *        Flags is ignored.
  394.  *        
  395.  *        These calls return the number of bytes sent, or -1 if an error 
  396.  *        occurred.
  397.  *        
  398.  *        EINVAL           The sum of the iov_len values in the iov array was
  399.  *                                 greater than 65535 (TCP) or 65507 (UDP) or there
  400.  *                       were too many entries in the array (16 for TCP or
  401.  *                       6 for UDP).
  402.  *
  403.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  404.  *        
  405.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  406.  *
  407.  *        ENOBUFS          The transmit queue is full. (UDP)
  408.  */
  409.  
  410. int UDPSocket::sendto(void * buffer, int count, int, void * to, int)
  411. {
  412.     miniwds      awds;
  413.     OSErr        err;
  414.     UDPiopb *    pb;
  415.  
  416.     /* make the stream if its not made already */
  417.     if (sstate == SOCK_STATE_NO_STREAM)
  418.         if (NewStream())
  419.             return -1;
  420.     
  421.     if (count > UDP_MAX_MSG)
  422.         return GUSI_error(EMSGSIZE);
  423.         
  424.     awds.terminus = 0;
  425.     awds.length = count;
  426.     awds.ptr = (char *) buffer;
  427.     
  428.     // if no address passed, hope we have one already in peer field
  429.     if (to == NULL)
  430.         if (peer.sin_len)
  431.             to = &peer;
  432.         else
  433.             return GUSI_error(EHOSTUNREACH);
  434.     
  435.     pb                                        = GetPB();
  436.     pb->ioCompletion                    = UDPIOCompletionUPP(&u_udp_send_done);
  437.     pb->csCode                             = UDPWrite;
  438.     pb->csParam.send.remoteHost     = ((struct sockaddr_in *)to)->sin_addr.s_addr;
  439.     pb->csParam.send.remotePort     = ((struct sockaddr_in *)to)->sin_port;
  440.     pb->csParam.send.wdsPtr         = (Ptr)&awds;
  441.     pb->csParam.send.checkSum         = true;
  442.     pb->csParam.send.sendLength     = 0/* must be zero */;
  443.     
  444.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  445.         return TCP_error(err);
  446.     
  447.     // get sneaky. compl. proc sets ptr to nil on completion, and puts result code in
  448.     // terminus field.
  449.     
  450.     SPIN(awds.ptr != NULL, SP_DGRAM_WRITE, count);
  451.     
  452.     if (awds.terminus < 0)
  453.         return TCP_error(awds.terminus);
  454.     else
  455.         return count;
  456. }
  457.  
  458. int UDPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  459. {
  460.     int        goodies     =     0;
  461.     
  462.     if (canRead || canWrite)
  463.         if (sstate == SOCK_STATE_NO_STREAM)
  464.             NewStream();
  465.                 
  466.     if (canRead)
  467.         if (asyncerr != 1) {
  468.             *canRead = true;
  469.             ++goodies;
  470.         }
  471.         
  472.     if (canWrite) {
  473.         *canWrite = true;
  474.         ++goodies;
  475.     }
  476.     
  477.     return goodies;
  478. }
  479.